package com.ug.common.utils;

import com.ug.common.constant.SecurityConstants;
import com.ug.common.enums.ErrorCode;
import com.ug.common.exception.SystemException;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.InvalidKeyException;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.util.CollectionUtils;

import javax.xml.bind.DatatypeConverter;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author LY
 * @create 2024/7/16 17:00
 * @Description jwt工具类
 */
public class JwtUtil {

    private static final Logger log = LoggerFactory.getLogger(JwtUtil.class);

    private static final byte[] secretKey = DatatypeConverter.parseBase64Binary(SecurityConstants.JWT_SECRET_KEY);

    private static final byte[] refreshKey = DatatypeConverter.parseBase64Binary(SecurityConstants.JWT_REFRESH_KEY);

    private JwtUtil(){
        throw new IllegalStateException("禁止创建当前对象");
    }

    /**
     * 根据用户和用户角色生成 token
     * @param userId 用户id
     * @param userName 用户名
     * @param roles 角色
     * @param isRemember 是否过期
     */
    public static String generateToken(Long userId, String userName, List<String> roles, Boolean isRemember){
        try {
            //  过期时间
            long expirationTime = isRemember ? SecurityConstants.TOKEN_EXPIRATION_REMEMBER_TIME : SecurityConstants.TOKEN_EXPIRATION_TIME;

            //  生成token
            return Jwts.builder()
                    //  生成签证信息
                    .setHeaderParam("typ", SecurityConstants.TOKEN_TYPE)
                    .signWith(Keys.hmacShaKeyFor(secretKey))
                    //  所有人
                    .setSubject(userName)
                    //  用户id
                    .claim(SecurityConstants.TOKEN_USER_ID_CLAIM, userId)
                    //  角色
                    .claim(SecurityConstants.TOKEN_ROLE_CLAIM, roles)
                    //  JWT主体
                    .setIssuer(SecurityConstants.TOKEN_ISSUER)
                    //  签发时间
                    .setIssuedAt(new Date())
                    .setAudience(SecurityConstants.TOKEN_AUDIENCE)
                    //  设置有效时间
                    .setExpiration(new Date(System.currentTimeMillis() + expirationTime))
                    .compact();
        } catch (InvalidKeyException e) {
            log.error("生成token异常：{}", e);
            throw new SystemException(ErrorCode.FAILURE.getCode(), e.getMessage());
        }
    }

    /**
     * 生成refresh_token
     * @param userName 用户名
     */
    public static String getRefreshToken(Long userId, String userName){
        return Jwts.builder()
                .signWith(Keys.hmacShaKeyFor(refreshKey))
                .setSubject(userName)
                .claim(SecurityConstants.TOKEN_USER_ID_CLAIM, userId)
                .setExpiration(new Date(System.currentTimeMillis() + SecurityConstants.TOKEN_EXPIRATION_REMEMBER_TIME))
                .compact();
    }

    /**
     * 校验token是否有效
     */
    public static boolean verifyToken(String token){
        try {
            getTokenBody(token);
            return true;
        } catch (ExpiredJwtException e) {
            log.warn("token 已过期 : {} failed : {}", token, e.getMessage());
            throw new SystemException(ErrorCode.TOKEN_EXPIRE);
        } catch (UnsupportedJwtException e) {
            log.warn("token 不支持 : {} failed : {}", token, e.getMessage());
            throw new SystemException(ErrorCode.TOKEN_NON);
        } catch (MalformedJwtException e) {
            log.warn("token 格式不正确 : {} failed : {}", token, e.getMessage());
            throw new SystemException(ErrorCode.TOKEN_INC_FOTMAT);
        } catch (SignatureException e) {
            log.warn("token 签名无效 : {} failed : {}", token, e.getMessage());
            throw new SystemException(ErrorCode.TOKEN_SIGN_EXPIRE);
        } catch (IllegalArgumentException e) {
            log.warn("token 不能为空 : {} failed : {}", token, e.getMessage());
            throw new SystemException(ErrorCode.TOKEN_NOT_NULL);
        }
    }

    /**
     * 根据token获取用户认证信息
     */
    public static Authentication getAuthentication(String token) {
        Claims claims;
        try {
            claims = getTokenBody(token);
        } catch (ExpiredJwtException e) {
            log.error("根据token获取用户认证信息异常：{}", e);
            claims = e.getClaims();
        }
        //  获取用户角色
        List<String> roles = claims.get(SecurityConstants.TOKEN_ROLE_CLAIM, List.class);
        List<SimpleGrantedAuthority> authorities =
                CollectionUtils.isEmpty(roles) ? Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")) :
                        roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());

        //  获取用户名
        User principal = new User(claims.getSubject(), "******", authorities);
        return new UsernamePasswordAuthenticationToken(principal, token, authorities);
    }

    /**
     * 从token中获取用户信息
     */
    private static Claims getTokenBody(String token){
        return Jwts.parserBuilder()
                .setSigningKey(secretKey)
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * 从refreshToken中获取用户信息
     */
    public static Claims getRefreshTokenBody(String refreshToken){
        return Jwts.parserBuilder()
                .setSigningKey(refreshKey)
                .build()
                .parseClaimsJws(refreshToken)
                .getBody();
    }

    /**
     * 获取token
     */
    public static String token(){
        HttpServletRequest request = RequestHolder.getHttpServletRequest();
        String authorization = request.getHeader(SecurityConstants.TOKEN_HEADER);
        if (StringUtils.isNotBlank(authorization) && authorization.startsWith(SecurityConstants.TOKEN_PREFIX)){
            return authorization.replace(SecurityConstants.TOKEN_PREFIX, "");
        }
        return null;
    }

    /**
     * 根据token获取用户id 解决security UserDetails无法获取用户id
     */
    public static Long getUserId(){
        String token = token();
        //  校验token
        if (verifyToken(token)){
            Claims claims = getTokenBody(token);
            return claims.get(SecurityConstants.TOKEN_USER_ID_CLAIM, Long.class);
        }
        return null;
    }
}
